﻿using GameCreator.Characters;
using Gp.Scripts.Core;
using System.Collections;
using UnityEngine;
using GameCreator.Core;
using Gp.Input;

#if UNITY_EDITOR
using UnityEditor;
#endif


namespace Gp.GCX {
    [AddComponentMenu("")]
    public class CharacterMoveAc : IAction {
        public enum MoveTo {
            Instant,
            WalkTo,
        }

        public PlaceHolder targetObj;

        public MoveTo moveTo = MoveTo.Instant;
        public bool waitUntilArrives = true;

        public Vector3 position;
        // public NavigationMarker marker;


        public bool cancelable = false;
        public float cancelDelay = 1.0f;

        [Range(0.0f, 5.0f)] [Tooltip("Threshold distance from the target that is considered as reached")]
        public float stopThreshold = 0.0f;

        private Character character = null;
        private bool forceStop = false;

        private bool wasControllable = false;
        private bool isCharacterMoving = false;

        // EXECUTABLE: ----------------------------------------------------------------------------

        public override bool InstantExecute(GameObject target, IAction[] actions, int index) {
            try {
                character = targetObj.GetCharacter();
            }
            catch (CharacterNotFoundException e) {
                Debug.LogError(e.Message);
                return true;
            }


            // 移动模式为“直接移动”时，直接坐标赋值
            if (moveTo == MoveTo.Instant) {
                character.transform.position = targetObj.transform.position;
                return true;
            }

            if (waitUntilArrives) return false;

            
            
            character.characterLocomotion.SetTarget(targetObj.transform.position, null,
                Mathf.Max(0, this.stopThreshold));
            return true;
        }

        public override IEnumerator Execute(GameObject target, IAction[] actions, int index) {
            try {
                character = targetObj.GetCharacter();
            }
            catch (CharacterNotFoundException e) {
                Debug.LogError(e.Message);
                yield break;
            }

            this.forceStop = false;


            this.isCharacterMoving = true;
            this.wasControllable = this.character.characterLocomotion.isControllable;
            this.character.characterLocomotion.SetIsControllable(false);

            
            character.characterLocomotion.SetTarget(
                targetObj.transform.position,
                null,
                Mathf.Max(0, stopThreshold),
                CharacterArrivedCallback
                );

            
            bool canceled = false;
            float initTime = Time.time;

            while (this.isCharacterMoving && !canceled && !forceStop) {
                if (this.cancelable && (Time.time - initTime) >= this.cancelDelay) {
                    canceled = Global.Get<InputManager>().CancelButtonDown();
                }

                yield return null;
            }

            this.character.characterLocomotion.SetIsControllable(this.wasControllable);

            if (canceled) yield return 999999;
            else yield return 0;
        }

        public override void Stop() {
            this.forceStop = true;
            if (this.character == null) return;

            this.character.characterLocomotion.SetIsControllable(this.wasControllable);
            this.character.characterLocomotion.Stop();
        }

        public void CharacterArrivedCallback() {
            this.isCharacterMoving = false;
        }

        // private void GetTarget(Character targetCharacter, GameObject invoker,
        //     ref Vector3 cPosition, ref ILocomotionSystem.TargetRotation cRotation, ref float cStopThresh) {
        //     cStopThresh = 0.0f;
        //     // switch (this.moveTo) {
        //     //     case MoveTo.None:
        //     //         cPosition = this.targetObj.transform.position;
        //     //         break;
        //     //     case MoveTo.Position:
        //     //         cPosition = this.position;
        //     //         break;
        //     //     case MoveTo.Transform:
        //     //         cPosition = this.transform.position;
        //     //         break;
        //     //     case MoveTo.Marker:
        //     //         cPosition = this.marker.transform.position;
        //     //         cRotation = new ILocomotionSystem.TargetRotation(true, this.marker.transform.forward);
        //     //         cStopThresh = this.marker.stopThreshold;
        //     //         break;
        //     // }
        //
        //     cPosition = targetObj.transform.position;
        // }

        // +--------------------------------------------------------------------------------------+
        // | EDITOR                                                                               |
        // +--------------------------------------------------------------------------------------+

#if UNITY_EDITOR

        public static new string NAME = "Gp/角色/移动角色";
        private const string NODE_TITLE = "移动 {0} 至 {1} {2}";

        private static readonly GUIContent GC_CANCEL = new GUIContent("Cancelable");

        // PROPERTIES: ----------------------------------------------------------------------------

        private SerializedProperty spTarget;
        private SerializedProperty spMoveTo;
        private SerializedProperty spWaitUntilArrives;

        private SerializedProperty spStopThreshold;
        private SerializedProperty spCancelable;
        private SerializedProperty spCancelDelay;

        // INSPECTOR METHODS: ---------------------------------------------------------------------

        public override string GetNodeTitle() {
            string value = "";

            if (targetObj == null) return "Nothing to move";

            // switch (this.moveTo) {
            //     case MoveTo.Position:
            //         value = string.Format("({0},{1},{2})", this.position.x, this.position.y, this.position.z);
            //         break;
            //
            //     case MoveTo.Transform:
            //         value = (this.transform == null ? "nothing" : this.transform.gameObject.name);
            //         break;
            //     case MoveTo.Marker:
            //         value = (this.marker == null ? "nothing" : this.marker.gameObject.name);
            //         break;
            //     case MoveTo.None:
            //         value = "";
            //         break;
            // }

            return string.Format(
                NODE_TITLE,
                this.targetObj.Info.name,
                "占位符处",
                value
            );
        }

        protected override void OnEnableEditorChild() {
            this.spTarget = this.serializedObject.FindProperty("targetObj");
            this.spMoveTo = this.serializedObject.FindProperty("moveTo");
            this.spWaitUntilArrives = this.serializedObject.FindProperty("waitUntilArrives");
            // this.spPosition = this.serializedObject.FindProperty("position");
            // this.spTransform = this.serializedObject.FindProperty("transform");
            // this.spMarker = this.serializedObject.FindProperty("marker");
            // this.spVariable = this.serializedObject.FindProperty("variable");
            this.spStopThreshold = this.serializedObject.FindProperty("stopThreshold");
            this.spCancelable = this.serializedObject.FindProperty("cancelable");
            this.spCancelDelay = this.serializedObject.FindProperty("cancelDelay");
        }

        protected override void OnDisableEditorChild() {
            return;
        }

        public override void OnInspectorGUI() {
            this.serializedObject.Update();

            EditorGUILayout.PropertyField(this.spTarget, new GUIContent("角色占位符"));
            EditorGUILayout.Space();
            EditorGUILayout.PropertyField(this.spMoveTo, new GUIContent("移动方式"));
            // switch ((MoveTo) this.spMoveTo.intValue) {
            //     case MoveTo.Position:
            //         EditorGUILayout.PropertyField(this.spPosition);
            //         break;
            //     case MoveTo.Transform:
            //         EditorGUILayout.PropertyField(this.spTransform);
            //         break;
            //     case MoveTo.Marker:
            //         EditorGUILayout.PropertyField(this.spMarker);
            //         break;
            //     case MoveTo.None:
            //         break;
            //     default:
            //         throw new ArgumentOutOfRangeException();
            // }

            EditorGUILayout.Space();
            EditorGUILayout.PropertyField(this.spStopThreshold);
            EditorGUILayout.PropertyField(this.spWaitUntilArrives);
            if (this.spWaitUntilArrives.boolValue) {
                Rect rect = GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.textField);
                Rect rectLabel = new Rect(
                    rect.x,
                    rect.y,
                    EditorGUIUtility.labelWidth,
                    rect.height
                );
                Rect rectCancenable = new Rect(
                    rectLabel.x + rectLabel.width,
                    rectLabel.y,
                    20f,
                    rectLabel.height
                );
                Rect rectDelay = new Rect(
                    rectCancenable.x + rectCancenable.width,
                    rectCancenable.y,
                    rect.width - (rectLabel.width + rectCancenable.width),
                    rectCancenable.height
                );

                EditorGUI.LabelField(rectLabel, GC_CANCEL);
                EditorGUI.PropertyField(rectCancenable, this.spCancelable, GUIContent.none);

                EditorGUI.BeginDisabledGroup(!this.spCancelable.boolValue);
                EditorGUI.PropertyField(rectDelay, this.spCancelDelay, GUIContent.none);
                EditorGUI.EndDisabledGroup();
            }

            this.serializedObject.ApplyModifiedProperties();
        }

#endif
    }
}